package com.bjpowernode.future;

import com.bjpowernode.util.ThreadUtils;

import java.util.concurrent.*;

/**
 * @ClassName MyCompletableFuture
 * @Author Cat
 */
public class MyCompletableFuture {

    public static void main(String[] args) {
        completedFutureExample();
        System.out.println("---------------");
        runAsyncExample();
        System.out.println("---------------");
        thenApplyExample();
        System.out.println("---------------");
        thenApplyAsyncExample();
        System.out.println("---------------");
        thenApplyAsyncWithExecutorExample();
        System.out.println("---------------");
        thenAcceptExample();
        System.out.println("---------------");
        thenAcceptAsyncExample();
    }

    public static void completedFutureExample() {
        CompletableFuture cf = CompletableFuture.completedFuture("message");
        System.out.println(cf.isDone());
        System.out.println(cf.getNow(null));
    }

    public static void runAsyncExample() {
        CompletableFuture cf = CompletableFuture.runAsync(() -> {
            System.out.println(">>" + Thread.currentThread().isDaemon());
            ThreadUtils.sleep(1);
        });
        System.out.println(cf.isDone());
        ThreadUtils.sleep(2);
        System.out.println(cf.isDone());
    }

    public static void thenApplyExample() {
        CompletableFuture cf = CompletableFuture.completedFuture("message").thenApply(s -> {
            System.out.println(Thread.currentThread().isDaemon());
            return s.toUpperCase();
        });
        System.out.println(cf.getNow(null));
    }

    public static void thenApplyAsyncExample() {
        CompletableFuture cf = CompletableFuture.completedFuture("message").thenApplyAsync(s -> {
            System.out.println(Thread.currentThread().isDaemon());
            ThreadUtils.sleep(1);
            return s.toUpperCase();
        });
        System.out.println(cf.getNow(null));
        System.out.println(cf.join());
    }

    static ExecutorService executor = Executors.newFixedThreadPool(3, new ThreadFactory() {
        int count = 1;
        @Override
        public Thread newThread(Runnable runnable) {
            return new Thread(runnable, "custom-executor-" + count++);
        }
    });

    public static void thenApplyAsyncWithExecutorExample() {
        CompletableFuture cf = CompletableFuture.completedFuture("message").thenApplyAsync(s -> {
            System.out.println(Thread.currentThread().getName().startsWith("custom-executor-"));
            System.out.println(Thread.currentThread().isDaemon());
            ThreadUtils.sleep(1);
            return s.toUpperCase();
        }, executor);

        System.out.println(cf.getNow(null));
        System.out.println(cf.join());
    }

    public static void thenAcceptExample() {
        StringBuilder result = new StringBuilder();
        CompletableFuture.completedFuture("thenAccept message").thenAccept(s -> result.append(s));
        System.out.println(result.length() > 0);
    }

    public static void thenAcceptAsyncExample() {
        StringBuilder result = new StringBuilder();
        CompletableFuture cf = CompletableFuture.completedFuture("thenAcceptAsync message")
                .thenAcceptAsync(s -> result.append(s));
        cf.join();
        System.out.println(result.length() > 0);
    }

    public static void completeExceptionallyExample() {
        CompletableFuture cf = CompletableFuture.completedFuture("message").thenApplyAsync(String::toUpperCase);
        CompletableFuture exceptionHandler = cf.handle((s, th) -> { return (th != null) ? "message upon cancel" : ""; });
        cf.completeExceptionally(new RuntimeException("completed exceptionally"));
        System.out.println(cf.isCompletedExceptionally());
        try {
            cf.join();
            System.out.println("Should have thrown an exception");
        } catch(CompletionException ex) { // just for testing
            System.out.println(ex.getCause().getMessage());
        }
        System.out.println(exceptionHandler.join());
    }
}